2022
Jan
01

网页好读版

这篇文章说明如果 Mock private Method ,帮助你减化 unit test 范围,进而提升写测试的意愿与让测试码更好维护。

假如你想测试 class 中的 Method A ,而这个 Method A 分别又去 call Method B / C ,为了降低测试的范围,我们会希望 Mock Method B 和 C 的 return 值 (mock 是指模拟物件的行为)。

B / C 如果是 public 或是 protected method ,我们只要使用 mockito or spock 就能轻松做 到 mock,困难的是如果想 mock 的对象是 private method , private method 只有 object 自已可以 access ,其它 test framework 都没办法对 private method 下手,目前唯有 PowerMock ,它直接修改了 java class byte-code ,把 private 改成 public accessible,让测试码能直接操作 private method 。

Example
  1. Method A
  2. - Call private Method B
  3. - Call private Method C

powerMock 使用范例

  • 我有一个 class App.java,其中 log 是 public , logInternal 是我想 mock 的 private method
App.java
  1. package com;
  2.  
  3. public class App {
  4.  
  5. public App() {
  6.  
  7. }
  8.  
  9. public String log() {
  10. System.out.println("run log");
  11. return this.logInternal();
  12. }
  13.  
  14. private String logInternal() {
  15. System.out.println("run logInternal");
  16. return "internal";
  17. }
  18.  
  19. }
  • class AppTest.java: 需要使用 PowerMock 中的 createPartialMock / expectPrivate / replay 这 3 个
AppTest.java
  1. package com;
  2.  
  3. import org.junit.Test;
  4. import org.junit.runner.RunWith;
  5. import static org.junit.Assert.assertEquals;
  6.  
  7. import org.powermock.api.easymock.PowerMock;
  8. import org.powermock.core.classloader.annotations.PrepareForTest;
  9. import org.powermock.modules.junit4.PowerMockRunner;
  10.  
  11. @RunWith(PowerMockRunner.class)
  12. @PrepareForTest(App.class)
  13. public class AppTest {
  14.  
  15. @Test
  16. public void TestLog() {
  17. try {
  18. App app = PowerMock.createPartialMock(App.class, "logInternal");
  19. PowerMock.expectPrivate(app, "logInternal").andReturn("mock_result");
  20. PowerMock.replay(app);
  21. String ret = app.log();
  22. assertEquals("Result is " + ret, ret, "mock_result");
  23. } catch (Exception e) {
  24. e.printStackTrace();
  25. }
  26.  
  27.  
  28. }
  29. }
  • pom.xml dependency
Example
  1. <dependency>
  2. <groupId>org.powermock</groupId>
  3. <artifactId>powermock-module-junit4</artifactId>
  4. <version>2.0.9</version>
  5. <scope>test</scope>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.powermock</groupId>
  9. <artifactId>powermock-api-mockito2</artifactId>
  10. <version>2.0.9</version>
  11. <scope>test</scope>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.powermock</groupId>
  15. <artifactId>powermock-api-easymock</artifactId>
  16. <version>2.0.9</version>
  17. <scope>test</scope>
  18. </dependency>

build.gradle

使用 gradle 的话,build config 可以参考这份

Example
  1. plugins {
  2. id "application"
  3. id "java"
  4. }
  5.  
  6. application {
  7. mainClassName = 'com.App'
  8. }
  9.  
  10.  
  11. allprojects {
  12. repositories {
  13. mavenCentral()
  14. }
  15. }
  16.  
  17. dependencies {
  18. def powerMockVersion="2.0.9"
  19. implementation (
  20. "org.powermock:powermock-module-junit4:${powerMockVersion}",
  21. "org.powermock:powermock-api-mockito2:${powerMockVersion}",
  22. "org.powermock:powermock-api-easymock:${powerMockVersion}"
  23. )
  24. }

参考资料

网页好读版